package com.sh.data.engine.domain.normalization.standard.service.impl;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.sh.data.engine.common.enumDefinition.DSType;
import com.sh.data.engine.common.util.RedisKeyUtil;
import com.sh.data.engine.common.util.ServletUtils;
import com.sh.data.engine.domain.base.model.PageResult;
import com.sh.data.engine.domain.common.model.domain.KeyValueDomain;
import com.sh.data.engine.domain.common.service.CommonService;
import com.sh.data.engine.domain.dataasset.service.TblPropService;
import com.sh.data.engine.domain.integration.datasource.model.domain.DataSourceDomain;
import com.sh.data.engine.domain.integration.datasource.model.domain.DataSourceManagerDomain;
import com.sh.data.engine.domain.integration.datasource.model.domain.DataSourceMultiDomain;
import com.sh.data.engine.domain.integration.datasource.model.domain.DataSourceQueryDomain;
import com.sh.data.engine.domain.integration.datasource.service.DataSourceService;
import com.sh.data.engine.domain.normalization.standard.model.domain.*;
import com.sh.data.engine.domain.normalization.standard.model.enums.BoundStatusEnum;
import com.sh.data.engine.domain.normalization.standard.model.mapper.*;
import com.sh.data.engine.domain.normalization.standard.model.param.*;
import com.sh.data.engine.domain.normalization.standard.service.IDictionaryMappingReportService;
import com.sh.data.engine.domain.normalization.standard.service.IDictionaryMappingService;
import com.sh.data.engine.domain.normalization.standard.service.IDictionaryService;
import com.sh.data.engine.domain.shims.db.BaseDbManager;
import com.sh.data.engine.domain.shims.db.model.FieldInfoDomain;
import com.sh.data.engine.domain.shims.db.model.TableInfoDomain;
import com.sh.data.engine.domain.shims.postgresql.manager.PostgreSqlManager;
import com.sh.data.engine.domain.util.ConvertUtil;
import com.sh.data.engine.repository.dao.dataasset.entity.TblPropEntity;
import com.sh.data.engine.repository.dao.integration.datasource.entity.DataSourceEntity;
import com.sh.data.engine.repository.dao.normalization.standard.DictionaryMappingDetailMapper;
import com.sh.data.engine.repository.dao.normalization.standard.DictionaryMappingMapper;
import com.sh.data.engine.repository.dao.normalization.standard.DictionaryMappingReportMapper;
import com.sh.data.engine.repository.dao.normalization.standard.arg.DictionaryQueryArg;
import com.sh.data.engine.repository.dao.normalization.standard.entity.DictionaryEntity;
import com.sh.data.engine.repository.dao.normalization.standard.entity.DictionaryMappingDetailEntity;
import com.sh.data.engine.repository.dao.normalization.standard.entity.DictionaryMappingEntity;
import com.sh.data.engine.repository.dao.normalization.standard.entity.DictionaryMappingReportEntity;
import com.sh.data.engine.repository.dao.workspace.manager.entity.StorageEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author boyue.wjl
 */
@Service
@Slf4j
public class DictionaryMappingServiceImpl
    extends ServiceImpl<DictionaryMappingMapper, DictionaryMappingEntity>
    implements IDictionaryMappingService {

    @Autowired
    private DictionaryMappingMapper dictionaryMappingMapper;
    @Autowired
    private DictionaryMappingDetailMapper dictionaryMappingDetailMapper;
    @Autowired
    private DictionaryMappingReportMapper dictionaryMappingReportMapper;

    @Autowired
    private DictionaryMappingDetailServiceImpl dictionaryMappingDetailService;

    @Autowired
    private DictionaryMappingReportServiceImpl dictionaryMappingReportService;

    @Autowired
    private IDictionaryMappingReportService iDictionaryMappingReportService;

    @Autowired
    private IDictionaryService dictionaryService;

    //    @Autowired
//    private DataOwnerService departmentService;

    @Autowired
    private DataSourceService datasourceBaseService;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private CommonService commonService;

    @Autowired
    private DataSourceService dataSourceService;

    @Autowired
    private TblPropService tblPropService;

    @Autowired
    private DictionaryQueryArgMapper dictionaryQueryArgMapper;

    @Autowired
    private DictionaryMappingDetailDomainMapper dictionaryMappingDetailDomainMapper;

    @Autowired
    private DictionaryMappingEntityMapper dictionaryMappingEntityMapper;

    @Autowired
    private DictionaryMappingDomainMapper dictionaryMappingDomainMapper;

    @Autowired
    private DictionaryMappingDetailParamMapper dictionaryMappingDetailParamMapper;

    @Autowired
    private DictionaryMappingParamMapper dictionaryMappingParamMapper;

    @Autowired
    private GetDictionaryDomainMapper getDictionaryDomainMapper;

    @Override
    public PageResult<DictionaryMappingDomain> listDictionaryMapping(DictionaryQueryParam param) {

        Long projectId = ServletUtils.getProjectId();

        DictionaryQueryArg dictionaryQueryArg = dictionaryQueryArgMapper.map(param);
        dictionaryQueryArg.setProjectId(projectId);

        List<DictionaryByArgDomian> dictionaryMapList =
            dictionaryService.queryListByArg(dictionaryQueryArg);

        BigDecimal total = new BigDecimal(dictionaryMapList.size());
        BigDecimal pageSize = new BigDecimal(param.getPageSize());
        BigDecimal pages = total.divide(pageSize);
        Double ceil = Math.ceil(pages.doubleValue());

        List<DictionaryByArgDomian> dictionaryByArgDomians =
            dictionaryMapList.subList(
                (param.getPageNum() - 1) * param.getPageSize() > dictionaryMapList.size()
                    ? 0
                    : (param.getPageNum() - 1) * param.getPageSize(),
                param.getPageNum() * param.getPageSize() > dictionaryMapList.size()
                    ? dictionaryMapList.size()
                    : param.getPageNum() * param.getPageSize());

        List<Long> departmentIds = new ArrayList<>();

        List<DictionaryMappingDomain> responseList = new ArrayList<>();
        for (DictionaryByArgDomian dictionaryMap : dictionaryByArgDomians) {
            DictionaryMappingDomain domain = new DictionaryMappingDomain();
            domain.setDictId(dictionaryMap.getId());
            domain.setDictNameCn(dictionaryMap.getDictNameCn());
            domain.setDictNameEn(dictionaryMap.getDictNameEn());
            domain.setDictDataType(dictionaryMap.getDictDataType());
            domain.setDictDataLength(dictionaryMap.getDictDataLength());
            domain.setCreatorName(dictionaryMap.getCreatorName());
            Long departmentId = dictionaryMap.getDepartmentId();
            if (departmentId != null) {
                departmentIds.add(departmentId);
                domain.setDepartmentId(departmentId);
            }
            domain.setBoundCount(dictionaryMap.getBoundCount());
            domain.setNotBoundCount(dictionaryMap.getNotBoundCount());
            domain.setUpdateTime(dictionaryMap.getUpdateTime());
            responseList.add(domain);
        }
//        List<DataOwnerDomain> allByProjectId = departmentService.getAllByProjectId(projectId);
//        Map<Long, DataOwnerDomain> departmentMap =
//            allByProjectId.stream().collect(Collectors.toMap(i -> i.getId(), Function.identity()));
//        responseList.forEach(
//            response -> {
//                if (response.getDepartmentId() != null) {
//                    DataOwnerDomain dataOwnerDomain = departmentMap.get(response.getDepartmentId());
//                    if (dataOwnerDomain != null) {
//                        response.setDepartmentName(dataOwnerDomain.getOwnerName());
//                    }
//                }
//            });
        responseList = sortForMappingList(responseList, param);
        PageResult<DictionaryMappingDomain> responsePage = new PageResult<>(
            total.longValue(), responseList, param.getPageNum(), param.getPageSize());
        return responsePage;
    }

    @Override
    public PageResult<DictionaryMappingDetailDomain> detailListDictionaryMapping(
        DictionaryMappingDetailQueryParam param) {
        Long projectId = ServletUtils.getProjectId();
        List<DictionaryMappingDetailEntity> dictionaryEntityList =
            dictionaryMappingDetailService.queryList(param.getDictId(), param.getMapSource());
        long total = dictionaryEntityList.size();
        List<DictionaryMappingDetailDomain> responseList = new ArrayList<>();
        List<TblPropEntity> tblProps =
            tblPropService.getTblPropByProjectId(projectId);
        Map<Long, String> tblMap =
            tblProps.stream().collect(Collectors.toMap(TblPropEntity::getTblId, TblPropEntity::getTableName, (x, y) -> x));
        dictionaryEntityList.forEach(
            dictionaryMappingDetailEntity -> {
                DictionaryMappingDetailDomain domain = dictionaryMappingDetailDomainMapper.map(dictionaryMappingDetailEntity);

                Long tblId = dictionaryMappingDetailEntity.getTblId();
                if (tblId != null) {
                    String tblName = tblMap.get(tblId);
                    domain.setTblName(tblName);
                }
                responseList.add(domain);
            });

        PageResult<DictionaryMappingDetailDomain> pageResult = new PageResult<>(
            total, responseList.subList(
            (param.getPageNum() - 1) * param.getPageSize(),
            Math.min(param.getPageNum() * param.getPageSize(), responseList.size())), param.getPageNum(), param.getPageSize());
        return pageResult;
    }

    @Transactional(rollbackFor = Exception.class, transactionManager = "engineTransactionManager")
    @Override
    public void detailBound(MappingDetailParam param) {
        DictionaryMappingDetailEntity detailEntity = dictionaryMappingDetailService.detailBound(param);
        if (!Objects.equals(detailEntity.getBoundStatus(), param.getBoundStatus())) {
            if (Objects.equals(detailEntity.getBoundStatus(), BoundStatusEnum.BOUND.getVal())) {
                dictionaryMappingMapper.updateBoundCountById(detailEntity.getDictMappingId(), -1, 1);
            } else {
                dictionaryMappingMapper.updateBoundCountById(detailEntity.getDictMappingId(), 1, -1);
            }
        }
    }

    @Override
    public MappingReportInfoDomain reportInfo() {
        Long projectId = ServletUtils.getProjectId();
        List<DataSourceDomain> dataSources = datasourceBaseService.getAllByProject(projectId);
        List<KeyValueDomain> typeList = new ArrayList<>();
        List<Map<String, Object>> dictMappingExternalDbs = getExternalDbByProjectId(projectId);
        if (CollectionUtils.isNotEmpty(dataSources)) {
            dictMappingExternalDbs.forEach(
                exDb -> {
                    String id = MapUtils.getString(exDb, "id");
                    String ds_name = MapUtils.getString(exDb, "ds_name");
                    typeList.add(KeyValueDomain.init(id, ds_name));
                });
        }

        MappingReportInfoDomain response = new MappingReportInfoDomain();
        response.setLastSiftTime(dictionaryMappingReportService.getMaxUpdateTime(projectId));
        response.setTypeList(typeList);
        return response;
    }

    @Override
    public List<DictionaryDomain> getMissMapping(Long projectId, List<DictionaryDomain> changedDics) {
        // 获取已发布字典
        List<DictionaryDomain> realeseDic = dictionaryService.getRealeseDic(projectId);
        List<Long> realeseDicId = realeseDic.stream().map(i -> i.getId()).collect(Collectors.toList());
        LambdaQueryWrapper<DictionaryMappingEntity> query =
            new QueryWrapper<DictionaryMappingEntity>().lambda();
        query
            .eq(DictionaryMappingEntity::getProjectId, projectId)
            .eq(DictionaryMappingEntity::getRowState, 1);
        List<DictionaryMappingEntity> multi = dictionaryMappingMapper.selectList(query);
        List<Long> mapping = multi.stream().map(i -> i.getDictId()).collect(Collectors.toList());
        // 获取已发布标准，但没有映射记录的标准
        realeseDicId.removeAll(mapping);
        // 加上changeDics，去重
        List<DictionaryDomain> collect =
            realeseDic.stream()
                .filter(i -> realeseDic.contains(i.getId()))
                .collect(Collectors.toList());
        collect.addAll(changedDics);
        return collect.stream().distinct().collect(Collectors.toList());
    }

    public int updateBoundCountById(Long id, Integer boundCount, Integer notBoundCount) {
        return dictionaryMappingMapper.updateBoundCountById(id, boundCount, notBoundCount);
    }

    public List<Map<String, Object>> getExternalDbByProjectId(Long projectId) {
        DataSourceQueryDomain query = new DataSourceQueryDomain();
        query.setProjectId(projectId);
        query.setDsTypes(
            Arrays.asList(
                DSType.Hive.name(),
                DSType.MySQL.name(),
                DSType.SQLServer.name(),
                DSType.PostgreSQL.name(),
                DSType.DB2.name(),
                DSType.Oracle.name(),
                DSType.Sap.name(),
                DSType.DM.name(),
                DSType.KINGBASE8.name(),
                DSType.TiDB.name()));
        List<DataSourceMultiDomain> dsByParams = dataSourceService.getDSByParams(query);
        // FIXME 现在只显示底层存储
        // List<DataSourceMultiDomain> storage = dsByParams.stream().filter(i -> i.getIsSystem() ==
        // 1).collect(Collectors.toList());
        List<DataSourceMultiDomain> storage = dsByParams.stream().collect(Collectors.toList());
        ArrayList<Map<String, Object>> list = Lists.newArrayList();
        storage.forEach(
            param -> {
                Map<String, Object> map = Maps.newHashMap();
                map.put("id", param.getId());
                map.put("ds_name", param.getDsName());
                map.put("dsType", param.getDsType());
                map.put("jdbcUrl", param.getDsLink());
                map.put("username", param.getUsername());
                map.put("password", param.getPassword());
                map.put("dbName", param.getDbName());
                map.put("isSystem", param.getIsSystem());
                list.add(map);
            });
        return list;
    }

    public int saveDictionaryMappingByDictIds(Long projectId, List<Long> dictIds) {
        if (CollectionUtils.isEmpty(dictIds)) {
            return 0;
        }
        HashSet<Long> dicts = new HashSet<>(dictIds);
        List<DictionaryMappingEntity> dictionaryMappings = new ArrayList<>();
        LambdaQueryWrapper<DictionaryMappingEntity> eq = new LambdaQueryWrapper<DictionaryMappingEntity>()
            .eq(DictionaryMappingEntity::getProjectId, projectId)
            .eq(DictionaryMappingEntity::getRowState, 1);
        List<Long> mappings = this.list(eq).stream().map(DictionaryMappingEntity::getDictId).collect(Collectors.toList());
        dicts.stream()
            .forEach(
                dictId -> {
                    DictionaryEntity dictionary = new DictionaryEntity();
                    dictionary.setId(dictId);
                    DictionaryMappingEntity dme =
                        DictionaryMappingEntity.builder()
                            .rowState(1)
                            .projectId(projectId)
                            .dictId(dictId)
                            .notBoundCount(0)
                            .boundCount(0)
                            .mapSource(0) // 0-内部数据源 1-外部数据源
                            .build();
                    dme.setCreateTime(new Date());
                    dme.setUpdateTime(new Date());
                    if (!mappings.contains(dme.getDictId())) {
                        dictionaryMappings.add(dme);
                    }
                });
        this.saveBatch(dictionaryMappings);
        return dictionaryMappings.size();
    }

    public DictionaryMappingDomain getDictMappingByProjectAndDict(Long projectId, Long dictId) {
        DictionaryMappingEntity dictMapping =
            dictionaryMappingMapper.getDictMappingByProjectAndDict(projectId, dictId);
        return dictionaryMappingDomainMapper.map(dictMapping);
    }

    public int updateById(DictionaryMappingParam param) {
        if (null != param) {
            DictionaryMappingEntity mappingEntity = dictionaryMappingEntityMapper.map(param);
            mappingEntity.setUpdateTime(new Date());
            // return dictionaryMappingMapper.updateByPrimaryKeySelective(mappingEntity);
            return dictionaryMappingMapper.updateById(mappingEntity);
        }
        return 0;
    }

    public int deleteById(Long id) {
        if (null != id) {
            return dictionaryMappingMapper.deleteById(id);
        }
        return 0;
    }

    public List<TableInfoDomain> getHiveTablesForDictMapping(
        Long projectId, String dbName, Long lastDdlTime) {
        // BaseDbManager dbManager = commonService.getStorageDbManagerByProjctId(projectId);
        DataSourceEntity systemDataSourceDetail =
            dataSourceService.getSystemDataSourceDetail(projectId);
        DataSourceManagerDomain managerInfo =
            dataSourceService.getManagerInfo(systemDataSourceDetail.getId());
        BaseDbManager dbManager = managerInfo.getDbManager();
        List<TableInfoDomain> tableList = null;
        try {
            tableList = dbManager.getTableList(dbName);
        } catch (Exception e) {
            log.error("获取默认存储数据表失败，异常原因：{}", e.getMessage());
        }
        if (null != lastDdlTime) {
            tableList =
                tableList.stream()
                    .filter(
                        tb -> {
                            Long l = tb.getLastDdlTime();
                            if (Objects.nonNull(l)) {
                                l = tb.getLastDdlTime() / 1000;
                            }
                            return l != null && Long.valueOf(l).longValue() > lastDdlTime;
                        })
                    .collect(Collectors.toList());
        }
        return tableList;
    }

    public void handleDeletedHiveTables(Long projectId, String dbName) {
        List<TableInfoDomain> tableList;
        // BaseDbManager dbManager = commonService.getStorageDbManagerByProjctId(projectId);
        DataSourceDomain systemDataSourceByProjectId =
            dataSourceService.getSystemDataSourceByProjectId(projectId);
        DataSourceManagerDomain managerInfo =
            dataSourceService.getManagerInfo(systemDataSourceByProjectId.getId());
        BaseDbManager dbManager = managerInfo.getDbManager();
        StorageEntity storage = commonService.getStorage(projectId);
        try {

            tableList = dbManager.getTableList(dbName);
            for (TableInfoDomain table : tableList) {
                Long tblId = table.getTblId();
                TblPropEntity tblPropByTblName =
                    tblPropService.getTblPropByTblName(projectId, table.getTableName());
                table.setTblId(tblPropByTblName.getTblId());
            }
            if (dbManager instanceof PostgreSqlManager) {
                tableList =
                    tableList.stream()
                        .filter(i -> Objects.equals(i.getSchema(), storage.getRmdbs().getSchema()))
                        .collect(Collectors.toList());
            }
        } catch (Exception e) {
            log.error("dbname:{},获取数据表失败，异常原因：{}", dbName, e.getMessage());
            return;
        }
        List<String> hiveTblIds =
            tableList.stream()
                .filter(i -> i.getTblId() != null)
                .map(tb -> String.valueOf(tb.getTblId()))
                .collect(Collectors.toList());
        List<Long> mysqlTblIds = dictionaryMappingReportService.getTblIdsByProjectId(projectId);
        List<Long> deletedTblIds = new ArrayList<>();
        deletedTblIds.addAll(mysqlTblIds);
        deletedTblIds.removeAll(hiveTblIds);
        if (CollectionUtils.isEmpty(deletedTblIds)) {
            return;
        }
        List<DictionaryMappingDetailDomain> dmds =
            dictionaryMappingDetailService.getDetailByProjectAndMppingId(
                new ArrayList<>(deletedTblIds));
        if (CollectionUtils.isNotEmpty(dmds)) {
            List<Long> ids = new ArrayList<>();
            dmds.forEach(
                dmd -> {
                    Integer boundStatus = dmd.getBoundStatus();
                    Integer bound = boundStatus == 0 ? 0 : -1;
                    Integer notBound = boundStatus == 0 ? -1 : 0;
                    this.updateBoundCountById(dmd.getDictMappingId(), bound, notBound);
                    ids.add(dmd.getId());
                });
            dictionaryMappingDetailService.deleteByIds(ids);
        }
        log.info(
            "[字典映射-内部数据源]处理已删除的表，projectId:{},dbName:{},mysqlTblIds:{},hiveTblIds:{},deletedTblIds:{}",
            projectId,
            dbName,
            mysqlTblIds.toString(),
            hiveTblIds.toString(),
            deletedTblIds.toString());
        dictionaryMappingReportService.deleteByTblIdList(deletedTblIds);
    }

    public void handleDictMappingForExternalTables(Long projectId) {
        if (null == projectId) {
            return;
        }
        int batchSize = 200;
        List<Object> dmds = null;
        List<Object> dmrs = null;
        do {
            dmds =
                redisTemplate
                    .opsForList()
                    .range(RedisKeyUtil.getDictMappingDetailKeyByProjectId(projectId), 0, batchSize - 1);
            if (null != dmds && !dmds.isEmpty()) {
                try {
                    redisTemplate
                        .opsForList()
                        .trim(RedisKeyUtil.getDictMappingDetailKeyByProjectId(projectId), dmds.size(), -1);
                    List<Object> range =
                        redisTemplate
                            .opsForList()
                            .range(RedisKeyUtil.getDictMappingDetailKeyByProjectId(projectId), 0, -1);
                    dictionaryMappingDetailService.saveDictMappingDetailList(
                        ConvertUtil.copyProperties(dmds, DictionaryMappingDetailParam.class));
                } catch (Exception e) {
                    log.error("执行[字典映射-外部数据源]映射详情异常,异常原因:{}", e.getMessage());
                    e.printStackTrace();
                }
            }
        } while (null != dmds && dmds.size() == batchSize);
        do {
            dmrs =
                redisTemplate
                    .opsForList()
                    .range(RedisKeyUtil.getDictMappingReportKeyByProjectId(projectId), 0, batchSize - 1);
            if (null != dmrs && !dmrs.isEmpty()) {
                try {
                    redisTemplate
                        .opsForList()
                        .trim(RedisKeyUtil.getDictMappingReportKeyByProjectId(projectId), batchSize, -1);
                    List<Object> range =
                        redisTemplate
                            .opsForList()
                            .range(RedisKeyUtil.getDictMappingReportKeyByProjectId(projectId), 0, -1);
                    dictionaryMappingReportService.saveDictMappingReportList(
                        ConvertUtil.copyProperties(dmrs, DictionaryMappingReportParam.class));
                } catch (Exception e) {
                    log.error("执行[字典映射-外部数据源]映射报告异常,异常原因:{}", e.getMessage());
                    e.printStackTrace();
                }
            }
        } while (null != dmrs && dmrs.size() == batchSize);
    }

    public void tableDictMappingForExternal(DictMappingParam param) {
        Long projectId = param.getProjectId();
        Long databaseId = param.getDatabaseId();
        String tableName = param.getTableName();
        List<DictionaryDomain> dictionaries = param.getDicts();
        List<FieldInfoDomain> fields = param.getFields();
        if (CollectionUtils.isEmpty(fields)) {
            return;
        }
        Long uniqueId = IdUtil.getSnowflakeNextId();
        DictionaryMappingReportParam report =
            DictionaryMappingReportParam.builder()
                .projectId(projectId)
                .datasourceBaseId(databaseId)
                .tblId(uniqueId)
                .tableName(tableName)
                .tableDes(Optional.ofNullable(fields.get(0).getComment()).orElse(""))
                .hitCount(0)
                .fieldCount(fields.size())
                .tableFieldJson(
                    JSON.toJSONString(ConvertUtil.copyProperties(fields, FieldDomain.class)))
                .mapSource(1)
                .rowState(1)
                .createTime(new Date())
                .updateTime(new Date())
                .build();
        if (CollectionUtils.isNotEmpty(dictionaries)) {
      /*List<String> dictEnNames =
      dictionaries.stream().map(d -> d.getDictNameEn()).collect(Collectors.toList());*/

            dictionaries.stream()
                .forEach(
                    dict -> {
                        String dictNameCn = dict.getDictNameCn();
                        String dictNameEn = dict.getDictNameEn();
                        if (CollectionUtils.isNotEmpty(fields)) {
                            List<FieldInfoDomain> filterdList =
                                fields.stream()
                                    .filter(
                                        field -> dictNameEn.equalsIgnoreCase(field.getFieldName())
                              /*|| (dictNameCn.equals(field.getComment())
                              && !dictEnNames.contains(field.getFieldName()))*/)
                                    .collect(Collectors.toList());
                            if (CollectionUtils.isNotEmpty(filterdList)) {
                                List<DictionaryMappingDetailParam> dictionaryMappingDetails = new ArrayList<>();
                                filterdList.forEach(
                                    field -> {
                                        // 映射详情
                                        DictionaryMappingDetailParam dictionaryMappingDetail =
                                            DictionaryMappingDetailParam.builder()
                                                .projectId(projectId)
                                                // -1l表示映射详情来自外部数据源
                                                .dictMappingId(-1L)
                                                .tblId(uniqueId)
                                                .fieldName(Optional.ofNullable(field.getFieldName()).orElse(""))
                                                .fieldDes(Optional.ofNullable(field.getComment()).orElse(""))
                                                .dictId(dict.getId())
                                                .dictDataType(
                                                    Optional.ofNullable(field.getFieldType()).orElse(""))
                                                .dictDataLength(
                                                    Optional.ofNullable(field.getColumnSize()).orElse(0))
                                                .rowState(1)
                                                .mapSource(1)
                                                .createUserId(0L)
                                                .updateUserId(0L)
                                                .boundStatus(0)
                                                .createTime(new Date())
                                                .updateTime(new Date())
                                                .build();
                                        dictionaryMappingDetails.add(dictionaryMappingDetail);
                                        report.setHitCount(
                                            Optional.ofNullable(report.getHitCount()).orElse(0) + 1);
                                    });
                                redisTemplate
                                    .opsForList()
                                    .rightPushAll(
                                        RedisKeyUtil.getDictMappingDetailKeyByProjectId(projectId),
                                        dictionaryMappingDetails.toArray());
                                redisTemplate.expire(
                                    RedisKeyUtil.getDictMappingDetailKeyByProjectId(projectId),
                                    2,
                                    TimeUnit.MINUTES);
                            }
                        }
                    });
        }
        redisTemplate
            .opsForList()
            .rightPush(RedisKeyUtil.getDictMappingReportKeyByProjectId(projectId), report);
        redisTemplate.expire(
            RedisKeyUtil.getDictMappingDetailKeyByProjectId(projectId), 2, TimeUnit.MINUTES);
    }

    public void handleDictMappingAfterDictOrTableModified(DictMappingParam param) {

        Long projectId = param.getProjectId();
        Long tblId = param.getTblId();
        DictionaryMappingDomain dictionaryMapping = param.getDictionaryMapping();
        DictionaryMappingReportDomain dictionaryMappingReport = param.getDictionaryMappingReport();
        List<FieldInfoDomain> filterdList = param.getFields();
        DictionaryDomain dict = param.getDictionary();

        List<DictionaryMappingDetailDomain> dmds =
            dictionaryMappingDetailService.getDetailByProjectAndMppingId(
                projectId, dictionaryMapping.getId(), tblId);

        Set<String> dictionaryMappingDetailFields = new HashSet<>(),
            filteredFields = new HashSet<>(),
            resultSet = new HashSet<>();
        Map<String, DictionaryMappingDetailDomain> dmdMap = new HashMap<>();

        Map<String, FieldInfoDomain> filteredFieldMap = new HashMap<>();
        if (CollectionUtils.isNotEmpty(dmds)) {
            dictionaryMappingDetailFields =
                dmds.stream().map(d -> d.getFieldName()).collect(Collectors.toSet());
            dmdMap = dmds.stream().collect(Collectors.toMap(d -> d.getFieldName(), d -> d));
        }
        if (CollectionUtils.isNotEmpty(filterdList)) {
            filteredFields = filterdList.stream().map(d -> d.getFieldName()).collect(Collectors.toSet());
            filteredFieldMap =
                filterdList.stream().collect(Collectors.toMap(d -> d.getFieldName(), d -> d));
        }
        // 交集  更新
        resultSet.clear();
        resultSet.addAll(dictionaryMappingDetailFields);
        resultSet.retainAll(filteredFields);
        if (!resultSet.isEmpty()) {
            Map<String, DictionaryMappingDetailDomain> finalDmdMap = dmdMap;
            Map<String, FieldInfoDomain> finalFilteredFieldMap = filteredFieldMap;
            resultSet.forEach(
                r -> {
                    DictionaryMappingDetailDomain dictionaryMappingDetail = finalDmdMap.get(r);
                    dictionaryMappingDetail =
                        dictionaryMappingDetail.toBuilder()
                            .fieldName(r)
                            .fieldDes(finalFilteredFieldMap.get(r).getComment())
                            .dictDataType(finalFilteredFieldMap.get(r).getFieldType())
                            .dictDataLength(finalFilteredFieldMap.get(r).getColumnSize())
                            .build();
                    dictionaryMappingDetailService.updateById(
                        dictionaryMappingDetailParamMapper.map(dictionaryMappingDetail));
                });
        }
        // 差集  删除
        resultSet.clear();
        resultSet.addAll(dictionaryMappingDetailFields);
        resultSet.removeAll(filteredFields);
        if (!resultSet.isEmpty()) {
            Map<String, DictionaryMappingDetailDomain> finalDmdMap = dmdMap;
            Map<String, FieldInfoDomain> finalFilteredFieldMap = filteredFieldMap;
            resultSet.forEach(
                r -> {
                    DictionaryMappingDetailDomain dictionaryMappingDetail = finalDmdMap.get(r);
                    dictionaryMappingDetailService.deleteByPrimaryKey(dictionaryMappingDetail.getId());
                    Integer boundStatus = dictionaryMappingDetail.getBoundStatus();
                    // 更新主表
                    if (boundStatus == 0) {
                        dictionaryMapping.setNotBoundCount(
                            Optional.ofNullable(dictionaryMapping.getNotBoundCount()).orElse(0) - 1);
                    } else {
                        dictionaryMapping.setBoundCount(
                            Optional.ofNullable(dictionaryMapping.getBoundCount()).orElse(0) - 1);
                    }
                    // 映射报告主表
                    dictionaryMappingReport.setHitCount(
                        Optional.ofNullable(dictionaryMappingReport.getHitCount()).orElse(0) - 1);
                });
        }
        // 差集  新增
        resultSet.clear();
        resultSet.addAll(filteredFields);
        resultSet.removeAll(dictionaryMappingDetailFields);
        if (!resultSet.isEmpty()) {
            Map<String, DictionaryMappingDetailDomain> finalDmdMap = dmdMap;
            Map<String, FieldInfoDomain> finalFilteredFieldMap = filteredFieldMap;
            resultSet.forEach(
                r -> {
                    // 映射详情
                    DictionaryMappingDetailParam dictMappingDetail =
                        DictionaryMappingDetailParam.builder()
                            .projectId(projectId)
                            .dictMappingId(dictionaryMapping.getId())
                            .tblId(tblId)
                            .fieldName(finalFilteredFieldMap.get(r).getFieldName())
                            .fieldDes(finalFilteredFieldMap.get(r).getComment())
                            .dictId(dict.getId())
                            .dictDataType(finalFilteredFieldMap.get(r).getFieldType())
                            .dictDataLength(finalFilteredFieldMap.get(r).getColumnSize())
                            .rowState(1)
                            .createTime(new Date())
                            .updateTime(new Date())
                            .build();
                    // 插入单条详情
                    dictionaryMappingDetailService.saveDictMappingDetail(dictMappingDetail);
                    // 更新主表
                    dictionaryMapping.setNotBoundCount(
                        Optional.ofNullable(dictionaryMapping.getNotBoundCount()).orElse(0) + 1);
                    // 映射报告主表 fixme
                    dictionaryMappingReport.setHitCount(
                        Optional.ofNullable(dictionaryMappingReport.getHitCount()).orElse(0) + 1);
                });
        }

        // 更新2个主表
        dictionaryMapping.setUpdateTime(new Date());
        this.updateById(dictionaryMappingParamMapper.map(dictionaryMapping));
        // 映射报告主表
        dictionaryMappingReportService.updateHitCountById(
            dictionaryMappingReport.getId(), dictionaryMappingReport.getHitCount());
    }

    public Long countByProjectIdAndDictId(Long projecId, Long dictId) {
        return dictionaryMappingMapper.countByProjectIdAndDictId(projecId, dictId);
    }

    private List<DictionaryMappingDomain> sortForMappingList(
        List<DictionaryMappingDomain> responseList, DictionaryQueryParam param) {
        HashSet<Integer> flag = Sets.newHashSet();

        if (Objects.equals(param.getOrderField(), "notBoundCount")) {

            responseList =
                responseList.stream()
                    .sorted(
                        new Comparator<DictionaryMappingDomain>() {
                            @Override
                            public int compare(DictionaryMappingDomain o1, DictionaryMappingDomain o2) {
                                flag.add(o1.getNotBoundCount() - o2.getNotBoundCount());
                                return o1.getNotBoundCount() - o2.getNotBoundCount();
                            }
                        })
                    .collect(Collectors.toList());

            if (Objects.equals(param.getOrder(), "asc") && !(flag.size() == 1 && flag.contains(0))) {
                Collections.reverse(responseList);
            }
        }
        if (Objects.equals(param.getOrderField(), "boundCount")) {
            responseList =
                responseList.stream()
                    .sorted(
                        new Comparator<DictionaryMappingDomain>() {
                            @Override
                            public int compare(DictionaryMappingDomain o1, DictionaryMappingDomain o2) {
                                flag.add(o1.getBoundCount() - o2.getBoundCount());
                                return o1.getBoundCount() - o2.getBoundCount();
                            }
                        })
                    .collect(Collectors.toList());
            if (Objects.equals(param.getOrder(), "asc") && !(flag.size() == 1 && flag.contains(0))) {
                Collections.reverse(responseList);
            }
        }
        if (Objects.equals(param.getOrderField(), "updateTime")) {

            responseList =
                responseList.stream()
                    .sorted(
                        new Comparator<DictionaryMappingDomain>() {
                            @Override
                            public int compare(DictionaryMappingDomain o1, DictionaryMappingDomain o2) {
                                flag.add(o1.getUpdateTime().compareTo(o2.getUpdateTime()));
                                return o1.getUpdateTime().compareTo(o2.getUpdateTime());
                            }
                        })
                    .collect(Collectors.toList());
            if (Objects.equals(param.getOrder(), "asc") && !(flag.size() == 1 && flag.contains(0))) {
                Collections.reverse(responseList);
            }
        }
        return responseList;
    }

    @Override
    public List<GetDictionaryDomain> getDictionaryList(String dictNameEn, String dictNameCn) {

        List<DictionaryDomain> realeseDic =
            dictionaryService.getRealeseDicDetail(dictNameEn, dictNameCn);

        return getDictionaryDomainMapper.map(realeseDic);
    }

    @Override
    public DictionaryStatisticsDomain statistics() {

        Long projectId = ServletUtils.getProjectId();
        List<DictionaryEntity> listDicAll = dictionaryService.getListDicAll(projectId);

        List<DictionaryMappingReportEntity> tableList = dictionaryMappingReportMapper.selectList(null);

        List<DictionaryMappingDetailEntity> fieldList = dictionaryMappingDetailMapper.selectList(null);

        return DictionaryStatisticsDomain.newStatDomain(listDicAll, tableList, fieldList);
    }

    @Override
    public DictionaryPublishDomain dictionaryStatistics() {

        Long projectId = ServletUtils.getProjectId();
        List<DictionaryEntity> listDicAll = dictionaryService.getListDicAll(projectId);
        DictionaryPublishDomain dictionaryPublishDomain =
            DictionaryPublishDomain.newStatDomain(listDicAll);

        return dictionaryPublishDomain;
    }

    @Override
    public FormCheckDomain formCheckStatistics() {
        List<DictionaryMappingReportEntity> tableList = dictionaryMappingReportMapper.selectList(null);

        return FormCheckDomain.newStatDomain(tableList);
    }

    @Override
    public FieldCheckDomain fieldCheckStatistics() {
        List<DictionaryMappingDetailEntity> fieldList = dictionaryMappingDetailMapper.selectList(null);
        return FieldCheckDomain.newStatDomain(fieldList);
    }

}
